﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace Lesson15_UDP服务端_综合练习
{
    class ServerSocket
    {
        private Socket socket;
        private Dictionary<string, Client> clientDic = new Dictionary<string, Client>();
        //待移除的客户端
        private List<Client> delList = new List<Client>();
        private byte[] recvBytes = new byte[512];
        private bool isClose;


        public void CheckTimeOut(object obj)
        {
            List<string> delList = new List<string>();
            while (true)
            {
                Thread.Sleep(15*1000);
                foreach (Client client in clientDic.Values)
                {
                    if (client.CheckTimeOut())
                        delList.Add(client.ClientId);
                }
                for(int i=0; i< delList.Count; i++)
                    RemoteClient(delList[i]);
            }

        }

        public void Start(string ip, int port)
        {
            isClose = false;
            IPEndPoint ipPoint = new IPEndPoint(IPAddress.Parse(ip), port);
            socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
            try
            {
                socket.Bind(ipPoint);
                ThreadPool.QueueUserWorkItem(Receive);
                ThreadPool.QueueUserWorkItem(CheckTimeOut);
                Console.WriteLine("[ServerSocket][Start] success({0}:{1})!", ip, port);
            }
            catch (SocketException e)
            {
                Console.WriteLine("[ServerSocket][Start] error code:{0}", e.SocketErrorCode);
            }
        }

        public string ClientId(EndPoint point)
        {
            IPEndPoint endPoint = point as IPEndPoint;
            return string.Format("{0}:{1}", endPoint.Address, endPoint.Port);
        }

        public Client GetClient(EndPoint point)
        {
            string clientId = ClientId(point);
            if (!clientDic.ContainsKey(clientId))
                return null;
            Client client = clientDic[clientId];
            return client;
        }

        public void Receive(object obj)
        {
            if (socket == null)
                return;
            EndPoint ipPoint = new IPEndPoint(IPAddress.Any, 0);
            while (!isClose)
            {
                if (socket.Available > 0)
                {
                    lock (socket)
                    {
                        int recvLen = socket.ReceiveFrom(recvBytes, ref ipPoint);
                        Console.WriteLine("[ServerSocket][Receive] recvLen:{0}", recvLen);
                        string clientId = ClientId(ipPoint);
                        Client client = null;
                        if (!clientDic.ContainsKey(clientId))
                        {
                            IPEndPoint endPoint = ipPoint as IPEndPoint;
                            client = new Client(endPoint.Address.ToString(), endPoint.Port);
                            clientDic.Add(clientId, client);
                        }
                        else
                        {
                            client = clientDic[clientId];
                        }
                        client.ReceiveMsg(recvBytes);
                    }
                }
            }
        }


        public void SendTo(BaseMsg msg, IPEndPoint ipPoint)
        {
            try
            {
                lock (socket)
                {
                    socket.SendTo(msg.Writing(), ipPoint);
                } 
            }
            catch (SocketException e)
            {
                Console.WriteLine("[ServerSocket][SendTo] error code:{0} err:{1}", e.SocketErrorCode, e.Message);
            }
            catch(Exception e)
            {
                Console.WriteLine("[ServerSocket][SendTo] Writing err:{0}", e.Message);
            }
        }

        public void Close()
        {
            if (socket != null)
            {
                isClose = true;
                socket.Shutdown(SocketShutdown.Both);
                socket.Close();
                socket = null;
            }
        }

        public void Broadcast(BaseMsg msg)
        {
            foreach (Client client in clientDic.Values)
            {
                SendTo(msg, client.IPEndPoint);
            }
        }

        public void RemoteClient(string clientId)
        {
            if (!clientDic.ContainsKey(clientId))
                return;
            clientDic.Remove(clientId);
            Console.WriteLine("客户端[{0}]被移除了!", clientId);
        }

    }
}
